﻿namespace EFS.RC.CC.Presences.Operators.OfficeService
{
	/// <summary>
	/// HArdcoded list of applications and there usage
	/// </summary>
	internal class Team : EFS.Common.ISelfDescriptor
	{
		#region Composition

		// async access
		private object syncAccess = new object();

		// Agents presences
		private System.Collections.Generic.Dictionary<System.String, EFS.RC.CC.Presences.Operators.OfficeService.Agent> agentsPresences =
			new System.Collections.Generic.Dictionary<System.String, EFS.RC.CC.Presences.Operators.OfficeService.Agent>(System.StringComparer.OrdinalIgnoreCase);

		// Supervisor presences
		private System.Collections.Generic.Dictionary<System.String, EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor> supervisorsPresences =
			new System.Collections.Generic.Dictionary<System.String, EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor>(System.StringComparer.OrdinalIgnoreCase);

		// end point
		private EFS.RC.CC.Applications.EndPoints.OfficeService.Operators operatorsOfficeServiceEndPointApplication = null;

		// hunting queue
		private EFS.RC.CC.Tools.Hunters.Queue agentsHunterQueue = new Tools.Hunters.Queue();

		#endregion

		#region Delegates

		/// <summary>
		/// Agent method call
		/// </summary>
		public delegate void AgentCallback(EFS.RC.CC.Presences.Operators.OfficeService.Agent agentPresence);

		/// <summary>
		/// Supervisor method call
		/// </summary>
		public delegate void SupervisorCallback(EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisorPresence);

		/// <summary>
		/// Deployment changes watch
		/// </summary>
		public delegate void DeploymentLoad(EFS.RC.CC.Presences.Operators.OfficeService.Team operatorsTeam);

		/// <summary>
		/// Deployment watch delegate registrar
		/// </summary>
		public DeploymentLoad OnDeploymentLoad;

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="operatorsOfficeServiceEndPointApplication"></param>
		public Team(EFS.RC.CC.Applications.EndPoints.OfficeService.Operators operatorsOfficeServiceEndPointApplication)
		{
			// check
			EFS.Common.Verify.Reference(operatorsOfficeServiceEndPointApplication, "operatorsOfficeServiceEndPointApplication");

			// end point
			this.operatorsOfficeServiceEndPointApplication = operatorsOfficeServiceEndPointApplication;
			this.operatorsOfficeServiceEndPointApplication.OnDeploymentLoad += EndPointDeploymentLoad;
		}

		#endregion

		#region Usage

		/// <summary>
		/// hunters queue
		/// </summary>
		public EFS.RC.CC.Tools.Hunters.Queue AgentsHunterQueue
		{
			get { return agentsHunterQueue; }
		}

		/// <summary>
		/// End point
		/// </summary>
		public EFS.RC.CC.Applications.EndPoints.OfficeService.Operators OperatorsOfficeServiceEndPointApplication
		{
			get { return operatorsOfficeServiceEndPointApplication; }
		}

		/// <summary>
		/// Describe this
		/// </summary>
		/// <returns></returns>
		public System.String Describe()
		{
			return System.String.Format("EFS.RC.CC.Presences.Team: {0}", operatorsOfficeServiceEndPointApplication.Describe());
		}

		/// Get agent by sip
		public EFS.RC.CC.Presences.Operators.OfficeService.Agent GetAgentBySIP(System.String sip)
		{
			// check
			EFS.Common.Verify.String(sip, "sip");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (agentsPresences.ContainsKey(sip))
				{
					// return
					return agentsPresences[sip];
				}
			}

			// not deployed
			return default(EFS.RC.CC.Presences.Operators.OfficeService.Agent);
		}

		/// Get agent by tel
		public EFS.RC.CC.Presences.Operators.OfficeService.Agent GetAgentByTEL(System.String tel)
		{
			// check
			EFS.Common.Verify.String(tel, "tel");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// find guid
				foreach (EFS.RC.CC.Presences.Operators.OfficeService.Agent agentPresence in agentsPresences.Values)
				{
					if (agentPresence.User.TEL.Equals(tel, System.StringComparison.OrdinalIgnoreCase))
					{
						// return
						return agentPresence;
					}
				}
			}

			// not deployed
			return default(EFS.RC.CC.Presences.Operators.OfficeService.Agent);
		}


		/// Get agent by domain guid
		public EFS.RC.CC.Presences.Operators.OfficeService.Agent GetAgent(System.Guid objectGuid)
		{
			// check
			EFS.Common.Verify.Guid(objectGuid, "objectGuid");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// find guid
				foreach (EFS.RC.CC.Presences.Operators.OfficeService.Agent agentPresence in agentsPresences.Values)
				{
					if (agentPresence.User.ObjectGuid == objectGuid)
					{
						// return
						return agentPresence;
					}
				}
			}

			// not deployed
			return default(EFS.RC.CC.Presences.Operators.OfficeService.Agent);
		}

		/// Get supervisor by sip
		public EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor GetSupervisor(System.String sip)
		{
			// check
			EFS.Common.Verify.String(sip, "sip");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (supervisorsPresences.ContainsKey(sip))
				{
					// return
					return supervisorsPresences[sip];
				}
			}

			// not deployed
			return default(EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor);
		}

		/// Get supervisor by domain guid
		public EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor GetSupervisor(System.Guid objectGuid)
		{
			// check
			EFS.Common.Verify.Guid(objectGuid, "objectGuid");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// find guid
				foreach (EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisorPresence in supervisorsPresences.Values)
				{
					if (supervisorPresence.User.ObjectGuid == objectGuid)
					{
						// return
						return supervisorPresence;
					}
				}
			}

			// not deployed
			return default(EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor);
		}

		/// <summary>
		/// Build all agentsPresences message
		/// </summary>
		/// <returns></returns>
		public EFS.RC.CC.Messages.Agents.StatusList GetAgentsStatusMessage()
		{
			// get agentsPresences list
			EFS.RC.CC.Presences.Operators.OfficeService.Agent[] agentsPresenceArray = GetAgentsArray();

			// status list
			System.Collections.Generic.List<EFS.RC.CC.Messages.Agents.Status> agentStatusList =
				new System.Collections.Generic.List<EFS.RC.CC.Messages.Agents.Status>();

			// make status list
			foreach (EFS.RC.CC.Presences.Operators.OfficeService.Agent agentPresence in agentsPresenceArray)
			{
				agentStatusList.Add(agentPresence.GetStatusMessage());
			}

			// make array
			return new EFS.RC.CC.Messages.Agents.StatusList(agentStatusList.ToArray());
		}

		/// <summary>
		/// Call back function for each agent
		/// </summary>
		/// <param name="agentCallback"></param>
		public void ForEachAgent(AgentCallback agentCallback)
		{
			// check
			EFS.Common.Verify.Reference(agentCallback, "agentCallback");

			// get agentsPresences list
			EFS.RC.CC.Presences.Operators.OfficeService.Agent[] agentsPresenceArray = GetAgentsArray();

			// call all bask
			foreach (EFS.RC.CC.Presences.Operators.OfficeService.Agent agentPresence in agentsPresenceArray)
			{
				agentCallback(agentPresence);
			}
		}

		/// <summary>
		/// Call back function for each supervisor
		/// </summary>
		/// <param name="agentCallback"></param>
		public void ForEachSupervisor(SupervisorCallback supervisorCallback)
		{
			// check
			EFS.Common.Verify.Reference(supervisorCallback, "supervisorCallback");

			// get supers list
			EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor[] supervisorsPresenceArray = GetSupervisorsArray();

			// call all bask
			foreach (EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisorPresence in supervisorsPresenceArray)
			{
				supervisorCallback(supervisorPresence);
			}
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Deployment Load
		/// </summary>
		/// <returns></returns>
		private void EndPointDeploymentLoad(EFS.RC.CC.Applications.EndPoints.OfficeService.Operators operatorsOfficeServiceEndPointApplication)
		{
			// check
			EFS.Common.Verify.Reference(operatorsOfficeServiceEndPointApplication, "operatorsOfficeServiceEndPointApplication");

			// check 
			if (!this.operatorsOfficeServiceEndPointApplication.Equals(operatorsOfficeServiceEndPointApplication))
			{
				throw new System.InvalidOperationException("EndPointDeploymentLoad End Point is different");
			}

			#region Agents

			// agentsPresences 
			EFS.RC.CC.Deployment.Entities.Operators.Agent[] deploymentAgents = EFS.RC.CC.Deployment.Tables.Tools.Agents.GetByOfficeEndPointID(
				operatorsOfficeServiceEndPointApplication.ApplicationPresence.PresenceEndPointDeployment.ID.Value);

			// agentsPresences 
			System.Collections.Generic.Dictionary<System.Guid, EFS.RC.CC.Deployment.Entities.Operators.Agent> validDeploymentAgents =
				new System.Collections.Generic.Dictionary<System.Guid, EFS.RC.CC.Deployment.Entities.Operators.Agent>();

			// check list, remove from database if not in AD
			foreach (EFS.RC.CC.Deployment.Entities.Operators.Agent deploymentAgent in deploymentAgents)
			{
				// get ad user
				EFS.AD.User adUSer = operatorsOfficeServiceEndPointApplication.Domain.GetByGuid(deploymentAgent.DomainOid.Value);

				// check
				if (null == adUSer)
				{
					// update -> create
					EFS.RC.CC.Tools.Deployment.Remove.Agent agentDeploymentRemove = new EFS.RC.CC.Tools.Deployment.Remove.Agent(deploymentAgent.ID.Value);

					// execute remove
					agentDeploymentRemove.Execute();

					// all ok
					continue;
				}
				else
				{
					validDeploymentAgents.Add(adUSer.ObjectGuid, deploymentAgent);
				}
			}

			// get array
			EFS.RC.CC.Presences.Operators.OfficeService.Agent[] agentsPresenceArray = GetAgentsArray();

			// remove all not in valid list
			foreach (EFS.RC.CC.Presences.Operators.OfficeService.Agent agentPresence in agentsPresenceArray)
			{
				if (!validDeploymentAgents.ContainsKey(agentPresence.User.ObjectGuid))
				{
					// remove
					RemoveAgent(agentPresence);

					// decomission
					agentPresence.DecomissionAgent();
				}
			}

			// add new from valid list
			foreach (System.Collections.Generic.KeyValuePair<System.Guid, EFS.RC.CC.Deployment.Entities.Operators.Agent> kvp in validDeploymentAgents)
			{
				try
				{
					// try to get
					EFS.RC.CC.Presences.Operators.OfficeService.Agent agent = GetAgent(kvp.Key);

					// check
					if (null == agent)
					{
						// add
						AddAgent(kvp.Value);
					}
					else
					{
						// reload
						agent.ReloadDeployment();
					}
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}

			#endregion

			#region Supervisors

			// supervisorsPresences 
			EFS.RC.CC.Deployment.Entities.Operators.Supervisor[] deploymentSupervisors = EFS.RC.CC.Deployment.Tables.Tools.Supervisors.GetByOfficeEndPointID(
				operatorsOfficeServiceEndPointApplication.ApplicationPresence.PresenceEndPointDeployment.ID.Value);

			// supervisorsPresences 
			System.Collections.Generic.Dictionary<System.Guid, EFS.RC.CC.Deployment.Entities.Operators.Supervisor> validDeploymentSupervisors =
				new System.Collections.Generic.Dictionary<System.Guid, EFS.RC.CC.Deployment.Entities.Operators.Supervisor>();

			// check list, remove from database if not in AD
			foreach (EFS.RC.CC.Deployment.Entities.Operators.Supervisor deploymentSupervisor in deploymentSupervisors)
			{
				// get ad user
				EFS.AD.User adUSer = operatorsOfficeServiceEndPointApplication.Domain.GetByGuid(deploymentSupervisor.DomainOid.Value);

				// check
				if (null == adUSer)
				{
					// update -> create
					EFS.RC.CC.Tools.Deployment.Remove.Supervisor supervisorDeploymentRemove = new EFS.RC.CC.Tools.Deployment.Remove.Supervisor(deploymentSupervisor.ID.Value);

					// execute remove
					supervisorDeploymentRemove.Execute();

					// all ok
					continue;
				}
				else
				{
					validDeploymentSupervisors.Add(adUSer.ObjectGuid, deploymentSupervisor);
				}
			}

			// get currelt array
			EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor[] supervisorsPresenceArray = GetSupervisorsArray();

			// remove all not in valid list
			foreach (EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisorPresence in supervisorsPresenceArray)
			{
				// check valid
				if (!validDeploymentSupervisors.ContainsKey(supervisorPresence.User.ObjectGuid))
				{
					// remove
					RemoveSupervisor(supervisorPresence);
				}
			}

			// add new from valid list
			foreach (System.Collections.Generic.KeyValuePair<System.Guid, EFS.RC.CC.Deployment.Entities.Operators.Supervisor> kvp in validDeploymentSupervisors)
			{
				try
				{
					// try to get
					EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisor = GetSupervisor(kvp.Key);

					// check
					if (null == supervisor)
					{
						// add
						AddSupervisor(kvp.Value);
					}
					else
					{
						// reload
						supervisor.ReloadDeployment();
					}
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}

			#endregion

			#region Delegates

			// call any delegates
			if (null != OnDeploymentLoad)
			{
				try
				{
					OnDeploymentLoad(this);
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}

			#endregion
		}

		/// <summary>
		/// Get agentsPresences in independant array
		/// </summary>
		/// <returns></returns>
		private EFS.RC.CC.Presences.Operators.OfficeService.Agent[] GetAgentsArray()
		{
			// get array
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				EFS.RC.CC.Presences.Operators.OfficeService.Agent[] agentsPresenceArray = new EFS.RC.CC.Presences.Operators.OfficeService.Agent[agentsPresences.Values.Count];
				agentsPresences.Values.CopyTo(agentsPresenceArray, 0);
				return agentsPresenceArray;
			}
		}

		/// <summary>
		/// Get supervisorsPresences in independant array
		/// </summary>
		/// <returns></returns>
		private EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor[] GetSupervisorsArray()
		{
			// get array
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor[] supervisorsPresenceArray = new EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor[supervisorsPresences.Values.Count];
				supervisorsPresences.Values.CopyTo(supervisorsPresenceArray, 0);
				return supervisorsPresenceArray;
			} 
		}

		/// <summary>
		/// Try add new agent
		/// </summary>
		/// <param name="deploymentAgent"></param>
		private void AddAgent(EFS.RC.CC.Deployment.Entities.Operators.Agent deploymentAgent)
		{
			// check
			EFS.Common.Verify.Reference(deploymentAgent, "deploymentAgent");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				try
				{
					// make presence
					EFS.RC.CC.Presences.Operators.OfficeService.Agent agentPresence =
						new EFS.RC.CC.Presences.Operators.OfficeService.Agent(deploymentAgent, operatorsOfficeServiceEndPointApplication, agentsHunterQueue);

					// add
					agentsPresences.Add(agentPresence.User.SIP, agentPresence);
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}
		}

		/// Remove agent
		private void RemoveAgent(EFS.RC.CC.Presences.Operators.OfficeService.Agent agentPresence)
		{
			// check
			EFS.Common.Verify.Reference(agentPresence, "agentPresence");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (!agentsPresences.ContainsValue(agentPresence))
				{
					throw new System.InvalidOperationException("Agent not in list");
				}

				// return
				agentsPresences.Remove(agentPresence.User.SIP);
			}
		}

		/// <summary>
		/// Try add new supervisor
		/// </summary>
		/// <param name="deploymentSupervisor"></param>
		private void AddSupervisor(EFS.RC.CC.Deployment.Entities.Operators.Supervisor deploymentSupervisor)
		{
			// check
			EFS.Common.Verify.Reference(deploymentSupervisor, "deploymentSupervisor");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				try
				{
					// make presence
					EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisorPresence
						= new EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor(deploymentSupervisor, operatorsOfficeServiceEndPointApplication);

					// add
					supervisorsPresences.Add(supervisorPresence.User.SIP, supervisorPresence);
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}
		}

		/// Remove agnt if exists
		private void RemoveSupervisor(EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisorPresence)
		{
			// check
			EFS.Common.Verify.Reference(supervisorPresence, "supervisorPresence");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (!supervisorsPresences.ContainsValue(supervisorPresence))
				{
					throw new System.InvalidOperationException("Decomission supervisor not in list");
				}

				// return
				supervisorsPresences.Remove(supervisorPresence.User.SIP);
			}
		}

		#endregion Runtime
	}
}
